﻿/**
 * AviTagsWrapper is a wrapper for abcAVI written in C# 
 * Copyright (C) 2010 Chris Meyer
 * 
 * This file is part of AviTagsWrapper.
 * 
 * AviTagsWrapper is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * AviTagsWrapper is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with AviTagsWrapper.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Chris Meyer (chris.meyer@ngsoftonline.com)
 * http://ngsoftonline.com/
 * 
 **/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using abcAVI;

namespace AviTagsWrapper {

    public delegate void StatusUpdate(object sender, StatusEventArgs args);

    public class AviTags {

        private readonly ExtendedAVITagsClass aviTagsClass;
        private object aviInfo;

        public event StatusUpdate StatusChanged;

        private string status;
        public string FileName { get; private set; }
        public bool TagsLoaded { get; private set; }
        public bool ChangesSaved { get; private set; }
        public ExtendedInfoTags Tags { get; private set; }

        public AviTags() {
            aviTagsClass = new ExtendedAVITagsClass();
            ChangesSaved = true;
        }

        public string Status {
            get { return status; }
            private set {
                status = value;
                if(StatusChanged != null)
                    StatusChanged(this, new StatusEventArgs(status));
            }
        }

        public void LoadTags(string fileName) {
            FileName = fileName;
            int result = aviTagsClass.ReadAVITags(FileName, (CParse_Mode.PM_Ignore_Errors | CParse_Mode.PM_Tech_Info), 1, out aviInfo);
            Status = "Load Tags: " + aviTagsClass.ErrorCodeToStr(result);

            if(IsOkResult(result)) {
                TagsLoaded = true;
                PopulateExtendedInfoTags();
            }

        }

        private static bool IsOkResult(int result) {
            return result == (int)CParse_Errors.PE_Parse_Ok;
        }

        private string GetTag(CTags_ID id) {
            if(TagsLoaded)
                return (string)aviTagsClass.GetInfo(aviInfo, CInfo_Type_ID.IDI_INFO_Tags, (int)id);
            return null;
        }

        public void SetTag(string tagName, string value) {
            if(TagsLoaded) {
                PropertyInfo info = Util.FindTagProperty(tagName);

                if(info != null) {
                    info.SetValue(Tags, value, null);
                    SetTag(Util.GetTagIdType(info), value);
                }
            } else
                Status = "Set Tag: No tags have been loaded.";
        }

        private void SetTag(CTags_ID id, string value) {
            int result = aviTagsClass.SetInfo(ref aviInfo, CInfo_Type_ID.IDI_INFO_Tags, (int)id, value);
            Status = "Set Tag: " + aviTagsClass.ErrorCodeToStr(result);

            if(IsOkResult(result))
                ChangesSaved = false;

        }

        public void SaveChanges() {
            if(!ChangesSaved) {
                Status = "Save Changes: Saving...";
                int result = aviTagsClass.WriteAVITags(FileName, aviInfo);
                Status = "Save changes: " + aviTagsClass.ErrorCodeToStr(result);
                if(IsOkResult(result))
                    ChangesSaved = true;
            } else
                Status = "Save Changes: No changes...";
        }

        private void PopulateExtendedInfoTags() {

            ExtendedInfoTags tags = new ExtendedInfoTags();

            foreach(CTags_ID cTagsId in Enum.GetValues(typeof(CTags_ID))) {
                PropertyInfo info = Util.FindTagProperty(cTagsId);
                if(info != null)
                    info.SetValue(tags, GetTag(cTagsId), null);
            }

            Tags = tags;
        }


    }

    public class Util {

        private static readonly Assembly Assembly;
        static Util() {
            Assembly = Assembly.GetAssembly(typeof(ExtendedInfoTags));
        }

        public static PropertyInfo FindTagProperty(string name) {
            PropertyInfo propertyInfo = null;
            foreach(var property in GetExtendedInfoTagsProperties()) {
                if(property.Name == name)
                    propertyInfo = property;
            }
            return propertyInfo;
        }

        public static PropertyInfo FindTagProperty(CTags_ID tag) {
            PropertyInfo propertyInfo = null;
            foreach(var property in GetExtendedInfoTagsProperties()) {
                object[] customAttributes = property.GetCustomAttributes(typeof(TagIdAttribute), true);
                foreach(object attribute in customAttributes) {
                    TagIdAttribute tagIdAttribute = attribute as TagIdAttribute;
                    if((tagIdAttribute != null) && (tagIdAttribute.Type == tag)) {
                        propertyInfo = property;
                    }
                }
            }
            return propertyInfo;
        }

        public static CTags_ID GetTagIdType(PropertyInfo property) {
            object[] attributes = property.GetCustomAttributes(typeof(TagIdAttribute), true);
            if(attributes != null) {
                TagIdAttribute id = attributes.First() as TagIdAttribute;
                if(id != null)
                    return id.Type;
            }
            return 0;
        }

        public static IEnumerable<PropertyInfo> GetExtendedInfoTagsProperties() {
            return Assembly.GetTypes().Where(t => t.Name == "ExtendedInfoTags").First().GetProperties();
        }



    }

    public class StatusEventArgs : EventArgs {
        public string Msg;
        public StatusEventArgs(string msg) {
            Msg = msg;
        }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class TagIdAttribute : Attribute {
        public readonly CTags_ID Type;

        public TagIdAttribute(CTags_ID type) {
            Type = type;
        }
    }
}
